package v9.assist

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.Image
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.Process
import android.view.WindowManagerService
import sample.ui.Permissions
import sample.ui.Properties
import kotlinx.coroutines.script.C
import kotlinx.coroutines.script.Controller
import kotlinx.coroutines.script.tes.analysisEvent
import kotlinx.coroutines.script.tes.phoneDisplayMetrics
import kotlinx.coroutines.script.tes.phoneScreenHeight
import kotlinx.coroutines.script.tes.phoneScreenWidth
import kotlinx.coroutines.script.tes.printLog
import kotlinx.coroutines.script.tes.runOnUIThread
import kotlinx.coroutines.suspendCancellableCoroutine
import v9.assist.analyst.analysisScreenshot
import kotlin.coroutines.resume
import androidx.core.graphics.createBitmap

private val isAndroid8 by lazy { Build.VERSION.SDK_INT >= Build.VERSION_CODES.O }
private var mp: MediaProjection? = null
private var h: Handler? = null
private var ir: ImageReader? = null
private var vd: VirtualDisplay? = null
private var lastCaptureTs = 0L
private var captureScreenshotCallback: ((Bitmap?)->Unit)? = null
suspend fun captureScreenshot(): Bitmap? {
    return suspendCancellableCoroutine { continuation ->
        captureScreenshotCallback = { screenshot ->
            captureScreenshotCallback = null
            continuation.resume(screenshot?.copy(Bitmap.Config.ARGB_8888, false))
        }
    }
}

private const val screenshotInterval = 550L

@SuppressLint("WrongConstant")
suspend fun initializeScreenshot(context: Context) { // invoke 2ms
    if (mp != null) return
    printLog { "initializeScreenshot context ${WindowManagerService.ins}" }
    val mediaProjection = Permissions.requireMediaProjection(context) ?: return
    val handlerThread = HandlerThread("Capture", Process.THREAD_PRIORITY_BACKGROUND)
    handlerThread.start()
    val handler = Handler(handlerThread.looper)
    mediaProjection.registerCallback(object : MediaProjection.Callback() {
        override fun onStop() {
            analysisEvent("screenshot_callback_on_stop") // 这个估计从来没有执行过。 看打点很多 2,048 577 11.71%
            mediaProjection.unregisterCallback(this)
            if (mp == mediaProjection) runOnUIThread { releaseScreenshot() }
        }
    }, handler) //
    // PixelFormat.RGBA_8888  ImageFormat.JPEG
    // ImageFormat.JPEG The producer output buffer format 0x3 doesn't match the ImageReader's configured buffer format 0x21.
    // ImageFormat.RGB_565 The producer output buffer format 0x3 doesn't match the ImageReader's configured buffer format 0x4.
    // PixelFormat.RGB_888 = 0x3
    val imageReaderFormat = if (isAndroid8) PixelFormat.RGBA_8888 else PixelFormat.RGB_888
    val imageReader = ImageReader.newInstance(phoneScreenWidth, phoneScreenHeight, imageReaderFormat, 2)
    val virtualDisplay = mediaProjection.createVirtualDisplay(
        "mirror", phoneScreenWidth, phoneScreenHeight, phoneDisplayMetrics.densityDpi,
        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, imageReader.surface, null, null
    )
    imageReader.setOnImageAvailableListener({ // io thread
        val image: Image? = imageReader.acquireLatestImage()
        // 刷新频率 screenshotInterval
        if (Controller.isRunning && System.currentTimeMillis() - lastCaptureTs > screenshotInterval) {
            // update frame
            lastCaptureTs = System.currentTimeMillis() // 把analysis的时间排除掉.这样更加均匀
            val bitmap = parseToBitmap(image)?.also(::analysisScreenshot) // 耗时操作
            captureScreenshotCallback?.invoke(bitmap)
            if (bitmap?.isRecycled != true) bitmap?.recycle()
        }
        image?.close()
    }, handler)
    mp = mediaProjection
    h = handler
    ir = imageReader
    vd = virtualDisplay
    printLog { "initializeScreenshot completed" }
}

private fun releaseScreenshot() {
    mp?.stop()
    mp = null
    h?.looper?.quitSafely()
    h = null
    ir?.close()
    ir = null
    vd?.release()
    vd = null
    Permissions.releaseMediaProjection()
}

private fun parseToBitmap(image: Image?): Bitmap? {
    if (image == null) return null
    return try {
        if (isAndroid8) parseToBitmapByRGBA8888(image) else parseToBitmapByRGB888(image)
    } catch (e: Throwable) {
        e.printStackTrace() // java.lang.IllegalStateException Image is already closed. planes[0].buffer
        null
    }
}
private fun parseToBitmapByRGBA8888(image: Image): Bitmap {
    val width = image.width
    val height = image.height
    val planes = image.planes
    val buffer = planes[0].buffer
    val pixelStride = planes[0].pixelStride
    val rowStride = planes[0].rowStride
    val rowPadding = rowStride - pixelStride * width
    val bitmap = createBitmap(width + rowPadding / pixelStride, height)
    bitmap.copyPixelsFromBuffer(buffer)
    return bitmap
}

private fun parseToBitmapByRGB888(image: Image): Bitmap {
    val width = image.width
    val height = image.height
    val planes = image.planes
    val buffer = planes[0].buffer
    val pixelData = ByteArray(buffer.remaining())
    val colors = IntArray(pixelData.size / 3)
    var i = 0
    while (i < pixelData.size) {
        val r = (pixelData[i].toInt() and 0xFF)
        val g = (pixelData[i + 1].toInt() and 0xFF)
        val b = (pixelData[i + 2].toInt() and 0xFF)
        colors[i / 3] = Color.rgb(r, g, b)
        i += 3
    }
    return Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888)
}